﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using DiscoveryLogic.Analytics.BusinessLogic.Dedups;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Dedup;
using DiscoveryLogic.Analytics.DataObjects.Networks;
using DiscoveryLogic.Analytics.DataObjects.Rules;
using DiscoveryLogic.Common.Utilities;

namespace DiscoveryLogic.Analytics.BusinessLogic.Scenarios
{
    public class PersonNetworkBuilder
    {
        /// <summary>
        /// node is author, edge is # of shared pubs
        /// </summary>
        /// <param name="networkName"></param>
        /// <param name="keywords"></param>
        /// <param name="fromYr"></param>
        /// <param name="toYr"></param>
        /// <param name="authorDedupRule"></param>
        /// <returns></returns>
        public static Network BuildCoAuthorNetworkViaWebOfScience(
            string networkName, List<string> keywords, 
            int fromYr, int toYr, EntityDedupRule authorDedupRule)
        {
            Network network = new Network(networkName, new List<EntityBase>(), new List<Link>());
            string connStr = ConfigurationManager.ConnectionStrings["WebOfScience"].ConnectionString;
            SqlConnection conn=new SqlConnection(connStr);
            try
            {
                conn.Open();

                string txtSearch = string.Empty;
                foreach(string keyword in keywords)
                {
                    if (txtSearch.Length > 0)
                        txtSearch += " OR ";
                    txtSearch += string.Format("\"{0}\"", keyword);
                }
                

                string sql = @"
select authorid, sourceitemid 
from SOURCEITEMAUTHOR
where sourceitemid in (
select si.SourceItemId	
	from sourceitem si		
		join sourceissue sis on sis.sourceissueid = si.sourceissueid	
			and year(sis.dlpubdate) between {1} and {2} 
	where contains(si.*,'{0}'))";
                SqlDataAdapter da1 = new SqlDataAdapter(string.Format(sql, txtSearch, fromYr, toYr), conn);
                DataSet ds=new DataSet();
                da1.Fill(ds, "Pub_Authors");
                sql = @"select * from author where authorid in (select authorid 
from SOURCEITEMAUTHOR
where sourceitemid in (
select si.SourceItemId	
	from sourceitem si		
		join sourceissue sis on sis.sourceissueid = si.sourceissueid	
			and year(sis.dlpubdate) between {1} and {2}
	where contains(si.*,'{0}')))";
                SqlDataAdapter da2=new SqlDataAdapter(string.Format(sql, txtSearch, fromYr, toYr), conn);
                da2.Fill(ds, "Authors");

                foreach(DataRow dr in ds.Tables["Authors"].Rows)
                {
                    PersonDO author=new PersonDO();
                    author.PersonID = int.Parse(dr["AuthorID"].ToString());
                    author.LastName = dr["LastName"] == DBNull.Value ? null : dr["LastName"].ToString();
                    author.FirstName = dr["FirstName"] == DBNull.Value ? null : dr["FirstName"].ToString();
                    author.FullName = dr["AuthorName"] == DBNull.Value ? null : dr["AuthorName"].ToString();
                    if (string.IsNullOrEmpty(author.LastName) && !string.IsNullOrEmpty(author.FullName))
                    {
                        string ln=author.LastName;
                        string fn=author.FirstName;
                        ParseAuthorFullName(author.FullName, ref fn, ref ln);
                        author.LastName = ln;
                        author.FirstName = fn;
                    }
                    author.Email = dr["Email"] == DBNull.Value ? null : dr["Email"].ToString();
                    network.Records.Add(author);
                }
                Dictionary<int,List<int>> author_Pubs=new Dictionary<int, List<int>>();
                foreach(EntityBase entity in network.Records)
                {
                    int authorID1 = ((PersonDO) entity).PersonID;
                    DataRow[] drpubs = ds.Tables["Pub_Authors"].Select("AuthorID=" + authorID1);
                    List<int> pubIDs=new List<int>();
                    foreach(DataRow drpub in drpubs)
                    {
                        int pubID = int.Parse(drpub["SourceItemID"].ToString());
                        if(!pubIDs.Contains(pubID))
                            pubIDs.Add(pubID);
                    }
                    author_Pubs.Add(authorID1, pubIDs);
                }
                int[] authorIDs=new int[author_Pubs.Count];
                author_Pubs.Keys.CopyTo(authorIDs,0);
                for(int i=0;i<authorIDs.Length;i++)
                {
                    int authorID1 = authorIDs[i];
                    List<int> pubids1 = author_Pubs[authorID1];
                    for(int k=i+1;k<authorIDs.Length;k++)
                    {
                        int authorID2 = authorIDs[k];
                        List<int> pubIDs2 = author_Pubs[authorID2];
                        int shared = IntUtil.Intersection(pubids1, pubIDs2).Count;
                        if(shared>0)
                        {
                            Link link=new Link(typeof(PersonDO),typeof(PersonDO),
                                authorID1.ToString(),authorID2.ToString(),
                                1, shared, LinkType.CoAuthor, false);
                            network.Links.Add(link);
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
            }
            network.Save();
            NetworkDedupSetting dedupSetting=new NetworkDedupSetting();
            dedupSetting.DedupRules=new Dictionary<EntityType, string>();
            dedupSetting.DedupRules.Add(EntityType.Persons, authorDedupRule.RuleName);
            dedupSetting.NetworkName = network.NetworkName;
            dedupSetting.Save();
            NetworkDedupWorker dedupWorker=new NetworkDedupWorker(network, dedupSetting);
            network = dedupWorker.DedupNetwork();
            return network;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="networkName"></param>
        /// <param name="keywords"></param>
        /// <param name="fromYr"></param>
        /// <param name="toYr"></param>
        /// <param name="authorDedupRule"></param>
        /// <returns></returns>
        public static Network BuildCoAuthorNetworkViaWebOfScienceUsingFullTextSearch(
            string networkName, List<string> keywords,
            int fromYr, int toYr, EntityDedupRule authorDedupRule)
        {
            Network network = new Network(networkName, new List<EntityBase>(), new List<Link>());
            string connStr = ConfigurationManager.ConnectionStrings["WebOfScience"].ConnectionString;
            SqlConnection conn = new SqlConnection(connStr);
            try
            {
                conn.Open();

                string txtSearch = string.Empty;
                foreach (string keyword in keywords)
                {
                    if (txtSearch.Length > 0)
                        txtSearch += " OR ";
                     txtSearch += keyword;
                }


                string sql = @"
select authorid, sourceitemid 
from SOURCEITEMAUTHOR
where sourceitemid in (

SELECT 
	FT_TBL.SOURCEITEMID
FROM
	SOURCEITEM AS FT_TBL INNER JOIN 
	CONTAINSTABLE(
		DBO.SOURCEITEM,
		(ABSTRACT,ARTICLETITLE),
		'{0}') AS KEY_TBL 
		ON FT_TBL.SOURCEITEMID=KEY_TBL.[KEY]
	INNER JOIN SOURCEISSUE SIS ON SIS.SOURCEISSUEID=FT_TBL.SOURCEISSUEID 
WHERE 
	YEAR(SIS.DLPUBDATE) BETWEEN {1} AND {2}

)";
                SqlDataAdapter da1 = new SqlDataAdapter(string.Format(sql, txtSearch, fromYr, toYr), conn);
                DataSet ds = new DataSet();
                da1.Fill(ds, "Pub_Authors");
                sql = @"select * from author where authorid in (select authorid 
from SOURCEITEMAUTHOR
where sourceitemid in (

SELECT 
	FT_TBL.SOURCEITEMID
FROM
	SOURCEITEM AS FT_TBL INNER JOIN 
	CONTAINSTABLE(
		DBO.SOURCEITEM,
		(ABSTRACT,ARTICLETITLE),
		'{0}') AS KEY_TBL 
		ON FT_TBL.SOURCEITEMID=KEY_TBL.[KEY]
	INNER JOIN SOURCEISSUE SIS ON SIS.SOURCEISSUEID=FT_TBL.SOURCEISSUEID 
WHERE 
	YEAR(SIS.DLPUBDATE) BETWEEN {1} AND {2}

))";
                SqlDataAdapter da2 = new SqlDataAdapter(string.Format(sql, txtSearch, fromYr, toYr), conn);
                da2.Fill(ds, "Authors");

                foreach (DataRow dr in ds.Tables["Authors"].Rows)
                {
                    PersonDO author = new PersonDO();
                    author.PersonID = int.Parse(dr["AuthorID"].ToString());
                    author.LastName = dr["LastName"] == DBNull.Value ? null : dr["LastName"].ToString();
                    author.FirstName = dr["FirstName"] == DBNull.Value ? null : dr["FirstName"].ToString();
                    author.FullName = dr["AuthorName"] == DBNull.Value ? null : dr["AuthorName"].ToString();
                    if(string.IsNullOrEmpty(author.LastName) && !string.IsNullOrEmpty(author.FullName))
                    {
                        string ln = author.LastName;
                        string fn = author.FirstName;
                        ParseAuthorFullName(author.FullName, ref fn, ref ln);
                        author.LastName = ln;
                        author.FirstName = fn;
                    }
                    author.Email = dr["Email"] == DBNull.Value ? null : dr["Email"].ToString();
                    network.Records.Add(author);
                }
                Dictionary<int, List<int>> author_Pubs = new Dictionary<int, List<int>>();
                foreach (EntityBase entity in network.Records)
                {
                    int authorID1 = ((PersonDO)entity).PersonID;
                    DataRow[] drpubs = ds.Tables["Pub_Authors"].Select("AuthorID=" + authorID1);
                    List<int> pubIDs = new List<int>();
                    foreach (DataRow drpub in drpubs)
                    {
                        int pubID = int.Parse(drpub["SourceItemID"].ToString());
                        if (!pubIDs.Contains(pubID))
                            pubIDs.Add(pubID);
                    }
                    author_Pubs.Add(authorID1, pubIDs);
                }
                int[] authorIDs = new int[author_Pubs.Count];
                author_Pubs.Keys.CopyTo(authorIDs, 0);
                for (int i = 0; i < authorIDs.Length; i++)
                {
                    int authorID1 = authorIDs[i];
                    List<int> pubids1 = author_Pubs[authorID1];
                    for (int k = i + 1; k < authorIDs.Length; k++)
                    {
                        int authorID2 = authorIDs[k];
                        List<int> pubIDs2 = author_Pubs[authorID2];
                        int shared = IntUtil.Intersection(pubids1, pubIDs2).Count;
                        if (shared > 0)
                        {
                            Link link = new Link(typeof(PersonDO), typeof(PersonDO),
                                authorID1.ToString(), authorID2.ToString(),
                                1, shared, LinkType.CoAuthor, false);
                            network.Links.Add(link);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
            }
            network.Save();
            NetworkDedupSetting dedupSetting = new NetworkDedupSetting();
            dedupSetting.DedupRules = new Dictionary<EntityType, string>();
            dedupSetting.DedupRules.Add(EntityType.Persons, authorDedupRule.RuleName);
            dedupSetting.NetworkName = network.NetworkName;
            dedupSetting.Save();
            NetworkDedupWorker dedupWorker = new NetworkDedupWorker(network, dedupSetting);
            network = dedupWorker.DedupNetwork();
            return network;
        }

        public static Network BuildCoAuthorNetworkUsingMedlineMeshTerms(
            string networkName, List<List<string>> orMeshTerms,
            int fromYr, int toYr, EntityDedupRule authorDedupRule)
        {
            Network network=new Network(
                networkName, new List<EntityBase>(), new List<Link>());

            string connStr = ConfigurationManager.ConnectionStrings["Medline"].ConnectionString;
            SqlConnection conn=new SqlConnection(connStr);
            List<int> pmids=new List<int>();
            try
            {
                conn.Open();
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.Text;

                for (int i = 0; i < orMeshTerms.Count; i++)
                {
                    Dictionary<int, int> sharedPmids = new Dictionary<int, int>();
                    for (int k = 0; k < orMeshTerms[i].Count; k++)
                    {
                        string meshTerm = orMeshTerms[i][k];
                        string sql =
                            @"select 
distinct c.pmid
from 
Citation c,
CitationMesh cm 
where c.pmid=cm.pmid and cm.MeshDescriptorId in (
select MeshDescriptorID from meshdescriptor 
where meshdescriptor='{0}')
and year(c.DateCreated) between {1} and {2}";
                        sql = string.Format(sql, meshTerm, fromYr, toYr);
                        DataTable dt = DbUtil.GetTableDataBySQL(connStr, "PMIDs", sql);
                        foreach (DataRow dr in dt.Rows)
                        {
                            int pmid = int.Parse(dr["PMID"].ToString());
                            if (!sharedPmids.ContainsKey(pmid))
                                sharedPmids.Add(pmid, 1);
                            else
                                sharedPmids[pmid] = sharedPmids[pmid] + 1;
                        }
                    }
                    foreach (int pmid in sharedPmids.Keys)
                    {
                        if (sharedPmids[pmid] == orMeshTerms[i].Count)
                        {
                            if (!pmids.Contains(pmid))
                                pmids.Add(pmid);
                        }
                    }
                }
                if(pmids ==null || pmids.Count==0)
                    throw new Exception("Unable to find publications");

                Dictionary<int, PersonDO> authors = new Dictionary<int, PersonDO>();
                Dictionary<int, List<int>> authorPmids = new Dictionary<int, List<int>>();
                SqlDataReader reader = null;
                foreach (int pmid in pmids)
                {
                    string sql = string.Format("select a.* from author a, citationauthor ca where a.authorid=ca.authorid and ca.pmid={0}", pmid);
                    cmd.CommandText = sql;
                    reader = cmd.ExecuteReader();
                    while(reader.Read())
                    {
                        int authorID = int.Parse(reader["AuthorID"].ToString());
                        if(!authors.ContainsKey(authorID))
                        {
                            string lastName = reader["LastName"] == DBNull.Value ? null : reader["LastName"].ToString();
                            string firstName = reader["ForeName"] == DBNull.Value ? null : reader["ForeName"].ToString();
                            PersonDO author=new PersonDO();
                            author.PersonID = authorID;
                            author.LastName = lastName;
                            author.FirstName = firstName;
                            authors.Add(authorID,author);
                        }
                        if(authorPmids.ContainsKey(authorID))
                        {
                            List<int> pmidsForAuthor = authorPmids[authorID];
                            pmidsForAuthor.Add(pmid);
                            authorPmids[authorID] = pmidsForAuthor;
                        }
                        else
                        {
                            List<int> pmidsForAuthor=new List<int>();
                            pmidsForAuthor.Add(pmid);
                            authorPmids.Add(authorID, pmidsForAuthor);
                        }
                    }
                    reader.Close();
                }

                foreach(PersonDO author in authors.Values)
                {
                    network.Records.Add(author);
                }
                int[] authorIDs=new int[authors.Count];
                authors.Keys.CopyTo(authorIDs,0);
                for(int i=0;i<authorIDs.Length;i++)
                {
                    List<int> pmids1 = authorPmids[authorIDs[i]];
                    for(int k=i+1;k<authorIDs.Length;k++)
                    {
                        List<int> pmids2 = authorPmids[authorIDs[k]];
                        List<int> sharedPubIDs = IntUtil.Intersection(pmids1, pmids2);
                        if (sharedPubIDs != null && sharedPubIDs.Count>0)
                        {
                            Link sharedPubLink=new Link(
                                typeof(PersonDO), typeof(PersonDO),
                                authorIDs[i].ToString(), authorIDs[k].ToString(),
                                1, sharedPubIDs.Count, LinkType.CoAuthor, false);
                            network.Links.Add(sharedPubLink);
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
            }
            finally
            {
                conn.Close();
            }

            network.Save();
            //NetworkDedupSetting dedupSetting = new NetworkDedupSetting();
            //dedupSetting.DedupRules = new Dictionary<EntityType, string>();
            //dedupSetting.DedupRules.Add(EntityType.Persons, authorDedupRule.RuleName);
            //dedupSetting.NetworkName = network.NetworkName;
            //dedupSetting.Save();
            //NetworkDedupWorker dedupWorker = new NetworkDedupWorker(network, dedupSetting);
            //network = dedupWorker.DedupNetwork();
            return network;
        }

        public static Network BuildCoAuthorNetworkUsingMedlineMeshTerms 
            (string networkName, string selectPMIDSql, 
            EntityDedupRule authorDedupRule,
            int minPubsPerPerson)
        {
            Network network = new Network(
                networkName, new List<EntityBase>(), new List<Link>());

            string connStr = ConfigurationManager.ConnectionStrings["Medline"].ConnectionString;
            SqlConnection conn = new SqlConnection(connStr);
            List<int> pmids = new List<int>();
            try
            {
                conn.Open();
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = selectPMIDSql;
                SqlDataReader reader = cmd.ExecuteReader();
                while(reader.Read())
                {
                    int pmid = int.Parse(reader["PMID"].ToString());
                    if(!pmids.Contains(pmid))
                        pmids.Add(pmid);
                }
                reader.Close();
                
                if (pmids == null || pmids.Count == 0)
                    throw new Exception("Unable to find publications");

                Dictionary<int, PersonDO> authors = new Dictionary<int, PersonDO>();
                Dictionary<int, List<int>> authorPmids = new Dictionary<int, List<int>>();
                foreach (int pmid in pmids)
                {
                    string sql = string.Format("select a.* from author a, citationauthor ca where a.authorid=ca.authorid and ca.pmid={0}", pmid);
                    cmd.CommandText = sql;
                    reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        int authorID = int.Parse(reader["AuthorID"].ToString());
                        if (!authors.ContainsKey(authorID))
                        {
                            string lastName = reader["LastName"] == DBNull.Value ? null : reader["LastName"].ToString();
                            string firstName = reader["ForeName"] == DBNull.Value ? null : reader["ForeName"].ToString();
                            PersonDO author = new PersonDO();
                            author.PersonID = authorID;
                            author.LastName = lastName;
                            author.FirstName = firstName;
                            authors.Add(authorID, author);
                        }
                        if (authorPmids.ContainsKey(authorID))
                        {
                            List<int> pmidsForAuthor = authorPmids[authorID];
                            pmidsForAuthor.Add(pmid);
                            authorPmids[authorID] = pmidsForAuthor;
                        }
                        else
                        {
                            List<int> pmidsForAuthor = new List<int>();
                            pmidsForAuthor.Add(pmid);
                            authorPmids.Add(authorID, pmidsForAuthor);
                        }
                    }
                    reader.Close();
                }
                int[] authorIDs = new int[authors.Count];
                authors.Keys.CopyTo(authorIDs, 0);
                for (int i = 0; i < authorIDs.Length; i++)
                {
                    PersonDO author = authors[authorIDs[i]];
                    author.Score = 0;
                    authors[authorIDs[i]] = author;
                }
                foreach (PersonDO author in authors.Values)
                {
                    List<int> pmids1 = authorPmids[author.PersonID];
                    if(pmids1.Count < minPubsPerPerson)
                        continue;
                    network.Records.Add(author);
                }

                for (int i = 0; i < authorIDs.Length; i++)
                {
                    List<int> pmids1 = authorPmids[authorIDs[i]];
                    if(pmids1.Count<minPubsPerPerson)
                        continue;
                    for (int k = i + 1; k < authorIDs.Length; k++)
                    {
                        List<int> pmids2 = authorPmids[authorIDs[k]];
                        if(pmids2.Count < minPubsPerPerson)
                            continue;
                        List<int> sharedPubIDs = IntUtil.Intersection(pmids1, pmids2);
                        if (sharedPubIDs != null && sharedPubIDs.Count > 0)
                        {
                            Link sharedPubLink = new Link(
                                typeof(PersonDO), typeof(PersonDO),
                                authorIDs[i].ToString(), authorIDs[k].ToString(),
                                1, sharedPubIDs.Count, LinkType.CoAuthor, false);
                            network.Links.Add(sharedPubLink);
                            authors[authorIDs[i]].Score += sharedPubIDs.Count;
                            authors[authorIDs[k]].Score += sharedPubIDs.Count;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
            }
            finally
            {
                conn.Close();
            }

            network.Save();
            //NetworkDedupSetting dedupSetting = new NetworkDedupSetting();
            //dedupSetting.DedupRules = new Dictionary<EntityType, string>();
            //dedupSetting.DedupRules.Add(EntityType.Persons, authorDedupRule.RuleName);
            //dedupSetting.NetworkName = network.NetworkName;
            //dedupSetting.Save();
            //NetworkDedupWorker dedupWorker = new NetworkDedupWorker(network, dedupSetting);
            //network = dedupWorker.DedupNetwork();
            return network;
        }

        public static Network BuildCoAuthorNetworkWithMeshTermFlagsUsingMedlineMeshTerms
            (string networkName, string selectPMIDSql, 
            Dictionary<string,List<string>> flagMeshTermsMappings,
            EntityDedupRule authorDedupRule,
            int minPubsPerPerson)
        {
            Network network = new Network(
                networkName, new List<EntityBase>(), new List<Link>());

            string connStr = ConfigurationManager.ConnectionStrings["Medline"].ConnectionString;
            SqlConnection conn = new SqlConnection(connStr);
            List<int> pmids = new List<int>();
            try
            {
                conn.Open();
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = selectPMIDSql;
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    int pmid = int.Parse(reader["PMID"].ToString());
                    if (!pmids.Contains(pmid))
                        pmids.Add(pmid);
                }
                reader.Close();

                if (pmids == null || pmids.Count == 0)
                    throw new Exception("Unable to find publications");

                // mesh term flags
                List<string> distMeshTerms=new List<string>();
                foreach(string flag in flagMeshTermsMappings.Keys)
                {
                    foreach(string meshTerm in flagMeshTermsMappings[flag])
                    {
                        if(!distMeshTerms.Contains(meshTerm.ToLower()))
                            distMeshTerms.Add(meshTerm.ToLower());
                    }
                }
                Dictionary<int,List<string>> pmidMeshTerms=new Dictionary<int, List<string>>();
                foreach(string meshTerm in distMeshTerms)
                {
                   string sql = @"select 
distinct c.pmid
from 
Citation c,
CitationMesh cm 
where c.pmid=cm.pmid and cm.MeshDescriptorId in (
select MeshDescriptorID from meshdescriptor 
where meshdescriptor='{0}')";
                    sql = string.Format(sql, meshTerm);
                    cmd.CommandText = sql;
                    reader = cmd.ExecuteReader();
                    while(reader.Read())
                    {
                        int pmid = int.Parse(reader["pmid"].ToString());
                        if(!pmids.Contains(pmid))
                            continue;
                        if(pmidMeshTerms.ContainsKey(pmid))
                        {
                            List<string> meshTermsForPub = pmidMeshTerms[pmid];
                            if (!meshTermsForPub.Contains(meshTerm))
                                meshTermsForPub.Add(meshTerm);
                            pmidMeshTerms[pmid] = meshTermsForPub;
                        }
                        else
                        {
                            List<string> meshTermsForPub=new List<string>();
                            meshTermsForPub.Add(meshTerm);
                            pmidMeshTerms.Add(pmid, meshTermsForPub);
                        }
                    }
                    reader.Close();
                }

                Dictionary<int, PersonDO> authors = new Dictionary<int, PersonDO>();
                Dictionary<int, List<int>> authorPmids = new Dictionary<int, List<int>>();
                foreach (int pmid in pmids)
                {
                    string sql = string.Format("select a.* from author a, citationauthor ca where a.authorid=ca.authorid and ca.pmid={0}", pmid);
                    cmd.CommandText = sql;
                    reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        int authorID = int.Parse(reader["AuthorID"].ToString());
                        if (!authors.ContainsKey(authorID))
                        {
                            string lastName = reader["LastName"] == DBNull.Value ? null : reader["LastName"].ToString();
                            string firstName = reader["ForeName"] == DBNull.Value ? null : reader["ForeName"].ToString();
                            PersonDO author = new PersonDO();
                            author.PersonID = authorID;
                            author.LastName = lastName;
                            author.FirstName = firstName;
                            authors.Add(authorID, author);
                        }
                        if (authorPmids.ContainsKey(authorID))
                        {
                            List<int> pmidsForAuthor = authorPmids[authorID];
                            pmidsForAuthor.Add(pmid);
                            authorPmids[authorID] = pmidsForAuthor;
                        }
                        else
                        {
                            List<int> pmidsForAuthor = new List<int>();
                            pmidsForAuthor.Add(pmid);
                            authorPmids.Add(authorID, pmidsForAuthor);
                        }
                    }
                    reader.Close();
                }

                foreach (PersonDO author in authors.Values)
                {
                    List<int> pmidsForPerson = authorPmids[author.PersonID];
                    if(pmidsForPerson.Count < minPubsPerPerson)
                        continue;
                    author.Flag = string.Empty;
                    List<string> containedMeshTerms = new List<string>();
                    foreach (int pmid in pmidsForPerson)
                    {
                        if (pmidMeshTerms.ContainsKey(pmid))
                        {
                            foreach (string containedMeshTerm in pmidMeshTerms[pmid])
                            {
                                if (!containedMeshTerms.Contains(containedMeshTerm))
                                    containedMeshTerms.Add(containedMeshTerm);
                            }
                        }
                    }
                    foreach(string flag in flagMeshTermsMappings.Keys)
                    {
                        List<string> reqMeshTerms = flagMeshTermsMappings[flag];
                        if(reqMeshTerms.Count==containedMeshTerms.Count)
                        {
                            if(StringUtil.Intersection(containedMeshTerms, reqMeshTerms,0,0).Count==reqMeshTerms.Count)
                            {
                                author.Flag = flag;
                                break;
                            }
                        }
                    }
                    network.Records.Add(author);
                }
                int[] authorIDs = new int[authors.Count];
                authors.Keys.CopyTo(authorIDs, 0);
                for (int i = 0; i < authorIDs.Length; i++)
                {
                    PersonDO author = authors[authorIDs[i]];
                    author.Score = 0;
                    authors[authorIDs[i]] = author;
                }
                for (int i = 0; i < authorIDs.Length; i++)
                {
                    List<int> pmids1 = authorPmids[authorIDs[i]];
                    if(pmids1.Count<minPubsPerPerson)
                        continue;
                    for (int k = i + 1; k < authorIDs.Length; k++)
                    {
                        List<int> pmids2 = authorPmids[authorIDs[k]];
                        if(pmids2.Count<minPubsPerPerson)
                            continue;
                        List<int> sharedPubIDs = IntUtil.Intersection(pmids1, pmids2);
                        if (sharedPubIDs != null && sharedPubIDs.Count > 0)
                        {
                            Link sharedPubLink = new Link(
                                typeof(PersonDO), typeof(PersonDO),
                                authorIDs[i].ToString(), authorIDs[k].ToString(),
                                1, sharedPubIDs.Count, LinkType.CoAuthor, false);
                            network.Links.Add(sharedPubLink);
                            authors[authorIDs[i]].Score += sharedPubIDs.Count;
                            authors[authorIDs[k]].Score += sharedPubIDs.Count;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
            }
            finally
            {
                conn.Close();
            }

            network.Save();
            //NetworkDedupSetting dedupSetting = new NetworkDedupSetting();
            //dedupSetting.DedupRules = new Dictionary<EntityType, string>();
            //dedupSetting.DedupRules.Add(EntityType.Persons, authorDedupRule.RuleName);
            //dedupSetting.NetworkName = network.NetworkName;
            //dedupSetting.Save();
            //NetworkDedupWorker dedupWorker = new NetworkDedupWorker(network, dedupSetting);
            //network = dedupWorker.DedupNetwork();
            return network;
        }

        private static void ParseAuthorFullName(string fullName, ref string firstName, ref string lastName)
        {
            Regex nameRegex =new Regex(@"^(\S+),\s*(.+)$");
            Match nameMatch = nameRegex.Match(fullName);
            if(nameMatch.Success)
            {
                lastName = nameMatch.Groups[1].Value;
                firstName = nameMatch.Groups[2].Value;
            }
        }
    }
}
